"""
author：fc
date：  2021/10/7
"""
#
# 这是朴素贝叶斯的应用实例，以用来进行异常账户显示
#
import numpy as np




class NaiveBayesian:
    def __init__(self, alpha):
        self.classP = dict()
        self.classP_feature = dict()  # 存放标签下特诊值的均值和发叉
        self.alpha = alpha  # 平滑值，不设置平滑值可能导致p(x_i|y_k)=0

    # 数据集
    def createData(self):
        train_data = np.array([
            # 对应的属性为用户注册天数、活跃天数、购物次数、点击商品个数
            [320, 204, 198, 265], [253, 53, 15, 2243], [53, 32, 5, 325],
            [63, 50, 42, 98], [1302, 523, 202, 5430], [32, 22, 5, 143],
            [105, 85, 70, 322], [872, 730, 840, 2762], [16, 15, 13, 52], [92, 70, 21, 693]
        ])
        train_labels = np.array([1, 0, 0, 1, 0, 0, 1, 1, 1, 0])
        return train_data, train_labels
    # 计算特征值的均值和方差

    def calMuAndSigMa(self,feature):
        mu = np.mean(feature)
        sigma = np.std(feature)
        return (mu,sigma)

    # 训练朴素贝叶斯算法模型
    def train(self, data, labels):
        numData = len(labels)
        numFeatures = len(data[0])

        # 是异常用户的概率
        self.classP[1] = (
                (sum(labels) + self.alpha) * 1.0 / (numData + self.alpha * len(set(labels))) # set 去除重复元素，得到无序集
        )
        # 不是异常用户的概率
        self.classP[0] = 1 - self.classP[1]
        # 存放每个label下每个特征下对应的高斯分布中的均值和方差
        # {labels:{feature1:{mean:0.2,vat:0.8},fenture2:{},...fenturen:{} }
        self.classP_feature = dict()
        for c in set(labels):
            self.classP_feature[c] = {}
            for i in range(numFeatures):
                feature = data[np.equal(labels, c)][:, i]
                self.classP_feature[c][i] = self.calMuAndSigMa(feature)

    # 有一个新用户，特征数据如下134（注册天数）,84（活跃天数）,235（购物次数）,349（点击商品个数） ，进行预测
    def gaussian(self, mu, sigma, x):  # 计算 P(x_i|y_k)=1/(sigma*平方根（2π）)*e((x-u)^2/2*sigma的平方)，这就是一个高斯分布公式
        #return 1.0 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-(np.square(x - mu) )/ 2 * np.square(sigma))
        return 1.0 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-(x - mu)**2 / (2 * sigma**2))
        # 开始预测

    def predict(self, x):
        label = -1  # 初始化类别
        maxP = 0
        for key in self.classP.keys():
            label_p = self.classP[key]
            currentP = 1.0
            feature_p = self.classP_feature[key]
            j = 0
            for fb in feature_p.keys():
                currentP *= self.gaussian(feature_p[fb][0], feature_p[fb][1], x[j])
                j += 1
                # 如果计算概率大于初始最大概率，进行最大概率赋值和记录类别
            if currentP * label_p > maxP:
                 maxP = currentP * label_p
                 label = key
        return label


if __name__ == '__main__':
    nb = NaiveBayesian(1.0)
    data, labels = nb.createData()
    nb.train(data, labels)
    label = nb.predict(np.array([134, 84, 235, 349]))
    print("未知类型用户的行为数据为:[134,84,235,349]，该用户的可能类型为：{}".format(label))
